package top.chengdongqing.mishop.auth

import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Service
import top.chengdongqing.mishop.auth.exception.LoginException
import top.chengdongqing.mishop.auth.exception.PasswordResetException
import top.chengdongqing.mishop.auth.exception.SignupException
import top.chengdongqing.mishop.common.constants.DEFAULT_AVATAR_URL
import top.chengdongqing.mishop.common.constants.RegexPatterns
import top.chengdongqing.mishop.common.exception.NotAcceptableException
import top.chengdongqing.mishop.entities.User
import top.chengdongqing.mishop.repositories.UserRepository
import top.chengdongqing.mishop.utils.DesensitizingUtils
import top.chengdongqing.mishop.utils.JwtUtils
import top.chengdongqing.mishop.utils.VerificationCodeUtils

@Service
class AuthService(
    private val userRepository: UserRepository,
    private val passwordEncoder: PasswordEncoder,
    private val jwtUtils: JwtUtils,
    private val verificationCodeUtils: VerificationCodeUtils
) {

    fun loginByPassword(account: String, password: String): AuthVO {
        // 校验数据格式
        if (!RegexPatterns.PHONE_NUMBER.matches(account) && !RegexPatterns.EMAIL.matches(account)
            || !RegexPatterns.PASSWORD.matches(password)
        ) {
            throw LoginException("账号或密码错误")
        }

        // 查询账号是否存在
        val user = userRepository.findByPhoneNumberOrEmailAddress(account, account)
            ?: throw LoginException("账号或密码错误")
        // 比对密码哈希是否一致
        if (!passwordEncoder.matches(password, user.password)) {
            throw LoginException("账号或密码错误")
        }

        // 签发token
        val token = jwtUtils.generate(user.id)
        return AuthVO.of(user, token)
    }

    fun loginByVerificationCode(phoneNumber: String, verificationCode: String): AuthVO {
        // 校验验证码
        verificationCodeUtils.verify(phoneNumber, verificationCode) {
            LoginException("验证码错误")
        }

        // 查询帐号信息
        val user = userRepository.findByPhoneNumber(phoneNumber) ?: run {
            // 不存在则自动注册
            val user = User(
                phoneNumber = phoneNumber,
                name = buildInitialUsername(phoneNumber),
                avatarUrl = DEFAULT_AVATAR_URL
            )
            userRepository.save(user)
            user
        }

        // 签发token
        val token = jwtUtils.generate(user.id)
        return AuthVO.of(user, token)
    }

    fun signup(phoneNumber: String, verificationCode: String, password: String): AuthVO {
        // 校验验证码
        verificationCodeUtils.verify(phoneNumber, verificationCode) {
            SignupException("验证码错误")
        }
        // 校验密码格式
        if (!RegexPatterns.PASSWORD.matches(password)) {
            throw SignupException("密码格式错误")
        }
        // 查询该帐号是否已存在
        if (userRepository.existsByPhoneNumber(phoneNumber)) {
            throw SignupException("该手机号已被注册")
        }

        // 创建用户
        val user = User(
            phoneNumber = phoneNumber,
            name = buildInitialUsername(phoneNumber),
            avatarUrl = DEFAULT_AVATAR_URL,
            password = passwordEncoder.encode(password)
        )
        userRepository.save(user)

        // 签发token
        val token = jwtUtils.generate(user.id)
        return AuthVO.of(user, token)
    }

    fun resetPassword(account: String, verificationCode: String, newPassword: String) {
        // 校验验证码
        verificationCodeUtils.verify(account, verificationCode) {
            throw PasswordResetException("验证码错误")
        }
        // 校验密码格式
        if (!RegexPatterns.PASSWORD.matches(newPassword)) {
            throw PasswordResetException("密码格式错误")
        }

        // 查询用户信息
        val user = userRepository.findByPhoneNumberOrEmailAddress(account, account)
            ?: throw PasswordResetException("该账号不存在")

        // 重置密码
        user.password = passwordEncoder.encode(newPassword)
        userRepository.save(user)
    }

    fun refreshToken(token: String): Token {
        try {
            val claims = jwtUtils.parse(token)
            val userId = claims.subject.toInt()
            return jwtUtils.generateForRefresh(userId, claims.expiration)
        } catch (e: Exception) {
            throw NotAcceptableException("刷新令牌失败：无效令牌")
        }
    }
}

private fun buildInitialUsername(phoneNumber: String): String {
    return "用户${DesensitizingUtils.desensitizeAccount(phoneNumber)}"
}